# -*- coding: utf-8 -*-
'''
###########################################
#  Created on 2011-10-25
#  @author: cl.lam
#  Description:
###########################################
'''
from datetime import datetime as dt
import os
import sys
from sqlalchemy.sql.expression import and_
from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy import Table, ForeignKey, Column
from sqlalchemy.types import Integer, DateTime, Text
from sqlalchemy.orm import relation, synonym
from sys2do.util.logic_helper import getPermission


try:
    from hashlib import sha1
except ImportError:
    sys.exit( 'ImportError: No module named hashlib\n'
             'If you are on python2.4 this library is not part of python. '
             'Please install it. Example: easy_install hashlib' )


from sys2do.model import DeclarativeBase, metadata, DBSession
from interface import SysMixin, DBMixin


__all__ = ['User', 'Group', 'Permission', ]


# { Association tables


# This is the association table for the many-to-many relationship between
# groups and permissions. This is required by repoze.what.
group_permission_table = Table( 'system_group_permission', metadata,
    Column( 'group_id', Integer, ForeignKey( 'auth_group.id',
        onupdate = "CASCADE", ondelete = "CASCADE" ), primary_key = True ),
    Column( 'permission_id', Integer, ForeignKey( 'auth_permission.id',
        onupdate = "CASCADE", ondelete = "CASCADE" ), primary_key = True )
 )

# This is the association table for the many-to-many relationship between
# groups and members - this is, the memberships. It's required by repoze.what.
user_group_table = Table( 'auth_user_group', metadata,
    Column( 'user_id', Integer, ForeignKey( 'auth_user.id',
        onupdate = "CASCADE", ondelete = "CASCADE" ), primary_key = True ),
    Column( 'group_id', Integer, ForeignKey( 'auth_group.id',
        onupdate = "CASCADE", ondelete = "CASCADE" ), primary_key = True )
 )




class Group( DeclarativeBase, SysMixin, DBMixin ):
    __tablename__ = 'auth_group'

    id = Column( Integer, autoincrement = True, primary_key = True )
    name = Column( 'name', Text, nullable = False, doc = u'名称' )
    displayName = Column( 'display_name', Text, doc = u'显示名称' )
    desc = Column( 'desc', Text, doc = u'描述' )
    users = relation( 'User', secondary = user_group_table, backref = 'groups' )
    referID = Column( 'refer_id', Integer, default = None )

    def __repr__( self ): return self.displayName or self.name
    def __str__( self ): return self.displayName or self.name
    def __unicode__( self ): return self.displayName or self.name

    def populate( self ):
        return {
                "id" : self.id,
                "name" :self.name,
                "display_name" : self.displayName,
                "desc" : self.desc,
                }


    def editable( self ): return getPermission( 'GROUP_EDIT' )
    def deletable( self ): return len( self.users ) < 1 and getPermission( 'GROUP_DEL' )


# The 'info' argument we're passing to the email_address and password columns
# contain metadata that Rum (http://python-rum.org/) can use generate an
# admin interface for your models.
class User( DeclarativeBase, SysMixin, DBMixin ):
    __tablename__ = 'auth_user'

    id = Column( Integer, autoincrement = True, primary_key = True )
    name = Column( 'name', Text, unique = True, nullable = False, doc = u'名称' )
    email = Column( 'email', Text, doc = u'邮件' )
    _password = Column( 'password', Text, doc = u'密码' )
    phone = Column( 'phone', Text, doc = u'联系电话' )
    mobile = Column( 'mobile', Text, doc = u'手机' )
    imageURL = Column( 'image_url', Text )
    lastLogin = Column( 'last_login', DateTime, default = dt.now, doc = u'最后登录时间' )
    forget_pw_sha1 = Column( 'forget_pw_sha1', Text )
    forget_pw_time = Column( 'forget_pw_time', DateTime)

    def __repr__( self ): return self.name
    def __str__( self ): return self.name
    def __unicode__( self ): return self.name


    @property
    def permissions( self ):
        """Return a set of strings for the permissions granted."""
        perms = set()
        for g in self.groups:
            perms = perms | set( g.permissions )
        return perms

    @classmethod
    def byEmail( cls, email ):
        """Return the user object whose email address is ``email``."""
        return DBSession.query( cls ).filter( cls.email == email ).first()

    @classmethod
    def byName( cls, username ):
        """Return the user object whose user name is ``username``."""
        return DBSession.query( cls ).filter( cls.name == username ).first()


    def _setPassword( self, password ):
        """Hash ``password`` on the fly and store its hashed version."""
        hashed_password = password

        if isinstance( password, unicode ):
            password_8bit = password.encode( 'UTF-8' )
        else:
            password_8bit = password

        salt = sha1()
        salt.update( os.urandom( 60 ) )
        hash = sha1()
        hash.update( password_8bit + salt.hexdigest() )
        hashed_password = salt.hexdigest() + hash.hexdigest()

        # Make sure the hashed password is an UTF-8 object at the end of the
        # process because SQLAlchemy _wants_ a unicode object for Unicode
        # columns
        if not isinstance( hashed_password, unicode ):
            hashed_password = hashed_password.decode( 'UTF-8' )

        self._password = hashed_password

    def _getPassword( self ):
        """Return the hashed version of the password."""
        return self._password

    password = synonym( '_password', descriptor = property( _getPassword, _setPassword ) )

    # }

    def validatePassword( self, password ):
        """
        Check the password against existing credentials.

        :param password: the password that was provided by the user to
            try and authenticate. This is the clear text version that we will
            need to match against the hashed one in the database.
        :type password: unicode object.
        :return: Whether the password is valid.
        :rtype: bool

        """
        hashed_pass = sha1()
        hashed_pass.update( password + self.password[:40] )
        return self.password[40:] == hashed_pass.hexdigest()


    def populate( self ):
        return {
                "id"   : self.id,
                "name" : self.name,
                "email" : self.email,
                }


    @classmethod
    def identify( cls, value ):
        try:
            return DBSession.query( cls ).filter( and_( cls.active == 0, cls.name.match( value ) ) ).one()
        except NoResultFound as e:
            raise e
        except:
            return DBSession.query( cls ).filter( and_( cls.active == 0, cls.name == value ) ).one()


    def editable( self ): return getPermission( 'USER_EDIT' )
    def deletable( self ): return getPermission( 'USER_DEL' )



class Permission( DeclarativeBase, SysMixin, DBMixin ):
    __tablename__ = 'auth_permission'

    id = Column( Integer, autoincrement = True, primary_key = True )
    name = Column( 'name', Text, unique = True, nullable = False, doc = u'名称' )
    desc = Column( 'desc', Text, doc = u'描述' )
    groups = relation( Group, secondary = group_permission_table, backref = 'permissions' )


    def __repr__( self ): return self.name
    def __str__( self ): return self.name
    def __unicode__( self ): return self.name

    def deletable( self ): return False
